/*********************************************************************************
 *      Copyright:  (C) 2022 Li Yanting<liyanting2@163.com>
 *                  All rights reserved.
 *
 *       Filename:  main.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(05/06/2022)
 *         Author:  Li Yanting <liyanting2@163.com>
 *      ChangeLog:  1, Release initial version on "05/06/2022 06:05:30"
 *                 
 ********************************************************************************/
#include "main.h"

int main(int argc, char **argv)
{

    gsm_ctx_t        st_gsm;
    int              ch;
    int              i;
    int              rv;

    attr_ctx_t      *st_attr = NULL;
    char             sms_message[MAX_SEND_SMS_LEN] = {0};
    char             phone_number[32] = {0};
    int              chose;
    int              send_model;
    char             specify_number[32] = {0};

    pthread_t        tid;
    int              led_sw = 0;

    struct option    opts[] = {
        {"baudrate", required_argument, NULL, 'b'},
        {"databits", required_argument, NULL, 'd'},
        {"parity", required_argument, NULL, 'p'},
        {"stopbits", required_argument, NULL, 's'},
        {"serial_name", required_argument, NULL, 'n'},
        {"interval_time", required_argument, NULL, 't'},
        {"debug", no_argument, NULL, 'D'},
        {"help", no_argument, NULL, 'h'},
        {NULL, 0, NULL, 0}
    };

    st_attr = (attr_ctx_t*)malloc(sizeof(attr_ctx_t));
    memset(st_attr, 0, sizeof(attr_ctx_t));
    
    while((ch = getopt_long(argc, argv, "b:d:p:s:n:Dh", opts, NULL)) != -1)
    {
        switch(ch)
        {
            case 'b':
                st_attr->baudrate = atoi(optarg);
                break;
            case 'd':
                st_attr->databits = atoi(optarg);
                break;
            case 'p':
                st_attr->parity = optarg[0];
                break;
            case 's':
                st_attr->stopbits = atoi(optarg);
                break;
            case 'n':
                strncpy(st_attr->serial_name, optarg, sizeof(st_attr->serial_name));
                break;
            case 'D':
                level = LOG_LEVEL_DEBUG;
                break;
            case 'h':
                print_usage(argv[0]);
                return 0;
            default:
                print_usage(argv[0]);
                return 0;
        }
    }

    if( strlen(st_attr->serial_name) == 0 )
    {
        print_usage(argv[0]);
        return -1;
    }

    if( (logger_init(LOG_PATH, level)) < 0)
    {
        fprintf(stderr, "initial logger system failure\n");
        return -2;
    }

    signal(SIGINT, prog_stop);

    /* open and init serial */
    if(serial_open(st_attr) < 0)
    {
        log_error("open %s failure.\n", st_attr->serial_name);
        return -1;
    }
    log_info("open %s successfully\n", st_attr->serial_name);

    if(serial_init(st_attr) < 0)
    {
        log_error("serial init successfully.\n");
        goto cleanup;
    }
    log_info("serial init successfully.\n");
    
    /* check serial and sim */
    while(1)
    {
        memset(&st_gsm, 0, sizeof(gsm_ctx_t));
        rv = check_sim_allready(st_attr, &st_gsm);
        if( rv != GSM_STATUS_READY )
        {
            log_error("SMS card is not all ready: %d\n", rv);
        }
        log_info("serial and sim are all ready.\n");
        break;
    }
    
    /* open pthread */
    if( thread_start(&tid, thread_mqtt, (void *)st_attr) < 0 )
    {
        log_error("open switch thread failure.\n");
        goto cleanup;
    }
    log_info("open mqtt thread successful!\n");

    if( thread_start(&tid, thread_switch, (void *)st_attr) < 0 )
    {
        log_error("open switch thread failure.\n");
        goto cleanup;
    }
    log_info("open switch thread successful!\n");

    /* function body */
    while( !g_stop )
    {
        if(st_gsm.status == GSM_STATUS_INIT)
        {
            while(1)
            {
                rv = check_sim_allready(st_attr, &st_gsm);
                if( rv != GSM_STATUS_READY )
                {
                    log_error("SMS card is not all ready: %d\n", rv);
                    continue;
                }
                log_info("serial and sim are all ready.\n");
                break;
            }
        }

        print_help();
        
        scanf("%d", &chose);
        getchar();
        
        if( 1 == chose )
        {
            g_listen_stop = 1;
            usleep(1000);

            printf("start to get all sms...\n\n");
            if( display_all_sms(st_attr) < 0 )
            {
                printf("get all sms failure.\n");
                g_listen_stop = 0;
                continue;
            }
            g_listen_stop = 0;
            printf("get all sms successfully!\n");
        }

        else if(2 == chose)
        {
            g_listen_stop = 1;
            usleep(1000);

            printf("start to delete all sms...\n");
            if( delete_all_sms(st_attr) < 0 )
            {
                printf("delete all sms failure.\n");
                g_listen_stop = 0;
                continue;
            }
            g_listen_stop = 0;
            printf("delete all sms successful!\n");
        }
        else if(3 == chose)
        {
            memset(g_specify_num, 0, sizeof(g_specify_num));

            printf("prease input new specify number: ");
            fgets(g_specify_num, sizeof(g_specify_num), stdin);
            for(i = 0; i < sizeof(g_specify_num); i++)
            {
                if(g_specify_num[i] == 0x0a)
                {
                    g_specify_num[i] = 0;
                    break;
                }
            }
            printf("set new specify number: %s successfully!\n", g_specify_num);
        }
        else if(4 == chose)
        {
            printf("prease input new interval time (default: 5): ");
            scanf("%d", &g_interval_time);
            printf("prease input new threshold temperature (default: 30): ");
            scanf("%d", &g_threshold);
            printf("prease input new warn time(second) (default: 600): ");
            scanf("%d", &g_warn_time);
            printf("Configuration successful!\n");
        }
        else if(5 == chose)
        {
            led_sw = ~led_sw;
            led_sw = (led_sw == -1) ? 1 : 0;
            switch_led(led_sw);
            if(led_sw == 1)
            {
                printf("open led successfully!\n");
            }
            else
            {
                printf("close led successfully!\n");
            }
        }
        else if(0 == chose)
        {
            g_stop = 1;
        }
        else 
        {
            print_help();
            continue;
        }
    }

cleanup:
    serial_close(st_attr);
    return 0;
}

static inline void print_help()
{
    printf("\n");
    printf("*************************************\n");
    printf("** input <1> Read all sms           *\n");
    printf("** input <2> Delete all sms         *\n");
    printf("** input <3> Set new num            *\n");
    printf("** input <4> Configuration          *\n");
    printf("** input <5> Led switch             *\n");
    printf("** input <0> Exit                   *\n");
    printf("*************************************\n");
    printf("\n");
}

static inline void print_usage(char *proname)
{
    printf("-b(--baudrate): set baudrate\n");
    printf("-d(--databits): set databits\n");
    printf("-p(--parity): set Parity, 0 for no parity, 1 for odd parity, 2 for evev parity\n");
    printf("-s(--stopbits): set stopbits\n");
    printf("-n(--serialname): set the name of the serial port\n");
    printf("-D(--debug): debug mode\n");
    printf("-h(--help): print this help information.\n");
    printf("for example: %s -n /dev/ttyUSB0 -b 115200 -d 8 -p e -s 2\n", proname);
}

static inline void prog_stop(int sig_num)
{
    if(sig_num == SIGINT)
    {
        g_stop = 1;
    }
}

static inline void adjust_buf(char *buf)
{
    int i = strlen(buf);
    strcpy(&buf[i-1], "\r");
}

int identify_sms_type(char *sms, int sms_len)
{
    int i;
    
    for( i=0; i<=sms_len; i++ )
    {
        if( sms[i] > 0x7F)
        {
            return 1;
        }
    }

    return 0;
}

int thread_start(pthread_t *thread_id, THREAD_BODY *thread_workbody, void *thread_arg)
{
    int                  rv = -1;
    pthread_attr_t       thread_attr;

    if( pthread_attr_init(&thread_attr) )
    {
        log_error("pthread_attr_init() failure: %s\n", strerror(errno));
        goto cleanup;
    }
    if( pthread_attr_setstacksize(&thread_attr, 120*1024) )
    {
        log_error("pthread_attr_setstacksize() failure: %s\n", strerror(errno));
        goto cleanup;
    }
    if( pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED) )
    {
        log_error("pthread_attr_setdetachstate() failure: %s\n", strerror(errno));
        goto cleanup;
    }
    /* Create the thread */
    if( pthread_create(thread_id, &thread_attr, thread_workbody, thread_arg) )
    {
        log_error("Create thread failure: %s\n", strerror(errno));
        goto cleanup;
    }

    rv = 0;
 
cleanup:
    pthread_attr_destroy(&thread_attr);
    return rv;
}

void *thread_switch(void *ctx)
{
    attr_ctx_t   *st_attr = (attr_ctx_t *)ctx;
    char          num_buf[32] = {0};
    char          ctr_num_nuf[32] = {0};
    char          time_buf[32] = {0};
    char          utf8_buf[512] = {0};
    char          temp[32];

    int           rv;

    while(!g_stop)
    {
        while( !g_listen_stop )
        {
            memset(num_buf, 0, sizeof(num_buf));
            memset(ctr_num_nuf, 0, sizeof(ctr_num_nuf));
            memset(time_buf, 0, sizeof(time_buf));
            memset(utf8_buf, 0, sizeof(utf8_buf));

            rv = listen_sms(st_attr, g_specify_num, 1, num_buf, ctr_num_nuf, time_buf, utf8_buf);
            if(rv == 0)
            {
                printf("sms num: %s\n", num_buf);
                printf("specify num: %s\n", g_specify_num);
                printf("time: %s\n", time_buf);
                printf("sms: %s\n", utf8_buf);
                printf("\n");
                
                if( 0 == strcmp(utf8_buf, "open led") )
                {
                    switch_led(1);
                    memset(temp, 0, sizeof(temp));
                    strcpy(temp, g_specify_num);
                    send_pdu_sms(st_attr, temp, "已开灯");
                    printf("open led successfully!\n");
                    print_help();
                }
                else if( 0 == strcmp(utf8_buf, "close led") )
                {
                    switch_led(0);
                    memset(temp, 0, sizeof(temp));
                    strcpy(temp, g_specify_num);
                    send_pdu_sms(st_attr, temp, "已关灯");
                    printf("close led successfully!\n");
                    print_help();
                }
                else
                {
                    memset(temp, 0, sizeof(temp));
                    strcpy(temp, g_specify_num);
                    send_pdu_sms(st_attr, temp, "指令错误，请重新输入指令！");
                    printf("received error cmd, send remind sms successfully\n");
                    print_help();
                    continue;
                }
            }
        }
    }
    
    pthread_exit(NULL);

    return 0;

}

void *thread_mqtt(void *ctx)
{
    attr_ctx_t            *st_attr = (attr_ctx_t *)ctx;
    sqlite3               *db;
    char                   table_name[32] = "raspi_01";

    iotd_ctx_t             st_iotd;

    struct mosquitto      *mosq = NULL;

    char                   json_data[1024] = {0};
    char                   unsend_data[1024] = {0};
    char                  *time;
    long                   last_time;
    long                   current_time;
    long                   send_time;
    float                  temper;
    char                   sms[64] = {0};

    int                    flag_mosq = 0;
    int                    flag_temper = 0;
    int                    rv = 0;
    char                   temp[32];

    pthread_t              tid;

    sql_init(&db, table_name);

    memset(&st_iotd, 0, sizeof(st_iotd));
    get_ctx_conf(INI_PATH, &st_iotd);

    mosquitto_lib_init();
    mosq = mosquitto_new(st_iotd.st_mqtt.clientid, true, &flag_mosq);
    mosquitto_message_callback_set(mosq, message_callback);
    mosquitto_connect_callback_set(mosq, connect_callback);

    mosquitto_username_pw_set(mosq, st_iotd.st_mqtt.username, st_iotd.st_mqtt.passwd);
    mosquitto_connect(mosq, st_iotd.st_mqtt.host, st_iotd.st_mqtt.port, st_iotd.st_mqtt.alivetime);
    mosquitto_subscribe(mosq, NULL, st_iotd.st_mqtt.reply_topic, st_iotd.st_mqtt.Qos);
    mosquitto_loop_start(mosq);

    sleep(1);

    st_iotd.mosq = mosq;

    thread_start(&tid, thread_reconnect, &st_iotd);

    while( !g_stop )
    {
        gtime(&last_time);
        time = get_format_time(last_time);
        get_temperature(&temper);

        /* Judge whether the temperature is too high*/
        if(temper >= g_threshold && 0 == flag_temper)
        {
            g_listen_stop = 1;
            usleep(1000);
            send_time = last_time;
            snprintf(sms, sizeof(sms), "温度已达阈值：%f°C, 请及时处理！", temper);
            memset(temp, 0, sizeof(temp));
            strcpy(temp, g_specify_num);
            rv = send_pdu_sms(st_attr, temp, sms);
            if(rv < 0)
            {
                log_error("send warn sms failure.\n");
                flag_temper = 0;
            }
            else
            {
                printf("temperature reaches threshold, send warn sms successfully!\n");
                print_help();
                log_info("send warn sms successfully!\n");
                flag_temper = 1;
            }
            g_listen_stop = 0;
        }
        if(1 == flag_temper)
        {
            gtime(&current_time);
            if(current_time - send_time > g_warn_time)
            {
                log_info("send inerval time up\n");
                flag_temper = 0;
            }
        }

        strcpy(st_iotd.st_collect.time, time);
        st_iotd.st_collect.temper = temper;
        pack_data(&st_iotd, json_data);

        flag_mosq = 0;
        if(g_uploat_stop)
        {
            mosquitto_publish(mosq, NULL, st_iotd.st_mqtt.pub_topic, strlen(json_data)+1, json_data, st_iotd.st_mqtt.Qos, NULL);
            sleep(1);
            if( 0 == flag_mosq)
            {
                log_error("send data to mqtt failure.\n");
                sql_insert(db, table_name, st_iotd.st_collect.time, st_iotd.st_collect.temper);
            }
            log_info("send data to mqtt successfully!\n");
        }
        else
        {
            log_info("mqtt disconnect.\n");
            sql_insert(db, table_name, st_iotd.st_collect.time, st_iotd.st_collect.temper);
        }
        
        while( !g_stop )
        {
            gtime(&current_time);
            if(current_time - last_time < g_interval_time)
            {
                if( g_uploat_stop )
                {
                    if( sql_check(db, &st_iotd, table_name) >= 0 )
                    {
                        log_info("database have data.\n");
                        pack_data(&st_iotd, unsend_data);

                        flag_mosq = 0;
                        mosquitto_publish(mosq, NULL, st_iotd.st_mqtt.pub_topic, strlen(unsend_data)+1, unsend_data, st_iotd.st_mqtt.Qos, NULL);
                        sleep(1);
                        if( 1 == flag_mosq )
                        {
                            log_info("send database data to mqtt successfully.\n");
                            sql_delete(db, table_name);
                        }
                    }
                    else
                    {
                        log_info("database no data.\n");
                        sleep(1);
                    }
                }
                else
                {
                    log_info("mqtt disconnect.\n");
                    sleep(3);
                }
                
                continue;
            }
            break;
        }
    }
    mosquitto_lib_cleanup();
    mosquitto_destroy(mosq);
    mosquitto_loop_stop(mosq, true);

    return 0;
}

void *thread_reconnect(void *ctx)
{
    iotd_ctx_t   *st_iotd = (iotd_ctx_t *)ctx;

    while( !g_stop )
    {
        g_uploat_stop = 0;
        if( mosquitto_reconnect(st_iotd->mosq) == MOSQ_ERR_SUCCESS )
        {
            g_uploat_stop = 1;
        }
        sleep(3);
    }
    return 0;

}

void message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message)
{
    *(int *)obj = 1;
}
void connect_callback(struct mosquitto *mosq, void *obj, int rc)
{
    *(int *)obj = 1;
}

